package com.sun.springmvc.frmework;


import com.sun.springmvc.frmework.annotaations.*;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SunDispatcherServlet extends HttpServlet {

    private Properties properties = new Properties();
    private List<String> className = new ArrayList<>();
    private Map<String, Object> map = new HashMap<>();
    private List<Handler> handlerList = new ArrayList<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String url = req.getRequestURI();
        Handler handler = getHandler(url);
        if (handler == null) {
            resp.getWriter().write("404 页面无法找到");
            return;
        }

        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();

        Object[] objects = new Object[parameterTypes.length];

        Map<String, String[]> parameterMap = req.getParameterMap();
        Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
        String username = "";
        for (Map.Entry<String, String[]> entry : entries) {
            String join = StringUtils.join(entry.getValue(), ",");
            Map<String, Integer> indexMap = handler.getIndexMap();
            if (indexMap.containsKey(entry.getKey())) {
                Integer index = indexMap.get(entry.getKey());
                objects[index] = join;
                if ("username".equals(entry.getKey())) {
                    username = join;
                }
            }
        }

        if (handler.getIndexMap().containsKey(HttpServletRequest.class.getSimpleName())) {
            Integer integer = handler.getIndexMap().get(HttpServletRequest.class.getSimpleName());
            objects[integer] = req;
        }

        if (handler.getIndexMap().containsKey(HttpServletResponse.class.getSimpleName())) {
            Integer integer = handler.getIndexMap().get(HttpServletResponse.class.getSimpleName());
            objects[integer] = resp;
        }
        handler.invoke(resp, objects, username);
    }

    private Handler getHandler(String url) {
        for (Handler handler : handlerList) {
            Matcher matcher = handler.getPattern().matcher(url);
            if (matcher.matches()) {
                return handler;
            }
        }
        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        String location = config.getInitParameter("contextConfigLocation");
        //解析配置文件
        doAnalysisConfigureFile(location);
        //扫描对应包下所有的class文件
        doScanAllClass(properties.getProperty("scanPackage"));
        //初始化bean对象，放入容器
        doInstance();
        //依赖注入
        doAutoWired();
        //处理handlermapping 将url和处理器controller方法进行关联
        doHandlerMapping();
        System.out.println(map);

    }

    private void doHandlerMapping() {
        if (!map.isEmpty()) {
            Set<Map.Entry<String, Object>> entries = map.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                Class<?> aClass = entry.getValue().getClass();
                if (aClass.isAnnotationPresent(SunRequestMapping.class)) {
                    SunRequestMapping sunRequestMapping = aClass.getAnnotation(SunRequestMapping.class);
                    String baseUrl = sunRequestMapping.value(); // /demo
                    Method[] methods = aClass.getMethods();
                    for (Method method : methods) {
                        if (method.isAnnotationPresent(SunRequestMapping.class)) {
                            SunRequestMapping annotation = method.getAnnotation(SunRequestMapping.class);
                            String childUrl = annotation.value();
                            Handler handler = new Handler(entry.getValue(), method, Pattern.compile(baseUrl + childUrl));
                            Map<String, Integer> indexMap = handler.getIndexMap();
                            Parameter[] parameters = method.getParameters();
                            for (int i = 0; i < parameters.length; i++) {
                                Class<?> type = parameters[i].getType();
                                String name;
                                if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                                    name = type.getSimpleName();
                                } else {
                                    name = parameters[i].getName();
                                }
                                indexMap.put(name, i);
                            }
                            if (method.isAnnotationPresent(SunSecurity.class)) {
                                SunSecurity sunSecurity = method.getAnnotation(SunSecurity.class);
                                String[] value = sunSecurity.value();
                                List<String> list = Arrays.asList(value);
                                handler.setSecurityList(list);
                            }
                            handlerList.add(handler);
                        }
                    }
                }
            }
        }


    }

    //依赖注入
    private void doAutoWired() {
        if (!map.isEmpty()) {
            Set<Map.Entry<String, Object>> entries = map.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                Class aClass = entry.getValue().getClass();
                Field[] declaredFields = aClass.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    if (declaredField.isAnnotationPresent(SunAutoWired.class)) {
                        SunAutoWired sunAutoWired = declaredField.getAnnotation(SunAutoWired.class);
                        String value = sunAutoWired.value();
                        if ("".equals(value)) {
                            Class<?> type = declaredField.getType();
                            if (type.isInterface()) {
                                value = type.getName();
                            } else {
                                value = lowerFirst(type.getSimpleName());
                            }
                        }
                        declaredField.setAccessible(true);
                        try {
                            declaredField.set(entry.getValue(), map.get(value));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    //初始化bean对象，放入容器
    private void doInstance() {
        for (int i = 0; i < className.size(); i++) {
            String name = className.get(i);
            try {
                Class<?> aClass = Class.forName(name);
                if (aClass.isAnnotationPresent(SunController.class)) {
                    String classValue = lowerFirst(aClass.getSimpleName());
                    map.put(classValue, aClass.newInstance());
                } else if (aClass.isAnnotationPresent(SunService.class)) {
                    SunService sunService = aClass.getAnnotation(SunService.class);
                    String value = sunService.value();
                    if (!"".equals(value)) {
                        map.put(value, aClass.newInstance());
                    } else {
                        map.put(lowerFirst(aClass.getSimpleName()), aClass.newInstance());
                    }
                }

                Class<?>[] interfaces = aClass.getInterfaces();
                for (Class<?> anInterface : interfaces) {
                    //获取接口的全限定类名
                    String interfaceName = anInterface.getName();
                    map.put(interfaceName, aClass.newInstance());
                }

            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }

    }

    //扫描对应包下所有的class文件
    private void doScanAllClass(String scanPackage) {
        //获取项目的根目录
        String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
        path = path + scanPackage.replaceAll("\\.", "/");

        File packageFile = new File(path);
        File[] files = packageFile.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                doScanAllClass(scanPackage + "." + file.getName());
            } else {
                String name = file.getName().replace(".class", "");
                className.add(scanPackage + "." + name);
            }
        }
    }

    //解析配置文件
    private Properties doAnalysisConfigureFile(String location) {
        InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(location);
        try {
            properties.load(stream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return properties;
    }

    // 首字母小写方法
    public String lowerFirst(String str) {
        char[] chars = str.toCharArray();
        if ('A' <= chars[0] && chars[0] <= 'Z') {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }

}
